package com.jichangxiu.common.utils;

import cn.hutool.core.io.IoUtil;
import com.jichangxiu.common.constant.Constants;

import javax.crypto.Cipher;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RsaUtils {

    private static final String RSA_PUBLIC_PUB = "rsa_public.pub";

    private static final int DEFAULT_KEY_SIZE = 2048;

    public static PublicKey getPublicKey() throws Exception {
        return getPublicKey("rsa_public.pub");
    }

    public static PublicKey getPublicKey(String filename) throws Exception {
        return getPublicKey(readFile(filename));
    }

    public static PrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
        return getPrivateKey("private.key");
    }

    public static PrivateKey getPrivateKey(String filename) throws NoSuchAlgorithmException, InvalidKeySpecException {
        return getPrivateKey(readFile(filename));
    }

    private static PublicKey getPublicKey(byte[] bytes) throws Exception {
        return KeyFactory.getInstance(Constants.RSA).generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(bytes)));
    }

    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        return KeyFactory.getInstance(Constants.RSA).generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(bytes)));
    }

    public static KeyPair generateRsa(String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(Constants.RSA);
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        return keyPairGenerator.genKeyPair();
    }

    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
        generateKey(publicKeyFilename, privateKeyFilename, secret, DEFAULT_KEY_SIZE);
    }

    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
        KeyPair keyPair = generateRsa(secret, keySize);
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    public static String encrypt(String text) {
        try {
            PublicKey publicKey = RsaUtils.getPublicKey(RSA_PUBLIC_PUB);
            return encrypt(text, publicKey);
        } catch (Exception ex) {
            throw new RuntimeException("【RsaUtils】使用【RSA】加密字符串【" + text + "】异常", ex);
        }
    }

    public static String encrypt(String text, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(Constants.RSA);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] tempBytes = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(tempBytes);
        } catch (Exception ex) {
            throw new RuntimeException("【RsaUtils】使用【RSA】加密字符串【" + text + "】异常", ex);
        }
    }

    public static String decrypt(String secretText) {
        try {
            PrivateKey privateKey = RsaUtils.getPrivateKey("private.key");
            return decrypt(secretText, privateKey);
        } catch (Exception ex) {
            throw new RuntimeException("【RsaUtils】使用【RSA】解密字符串【" + secretText + "】异常", ex);
        }
    }

    public static String decrypt(String secretText, PrivateKey privateKey) {
        try {
            // 生成私钥
            Cipher cipher = Cipher.getInstance(Constants.RSA);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // 密文解码
            byte[] secretTextDecoded = Base64.getDecoder().decode(secretText.getBytes(StandardCharsets.UTF_8));
            byte[] tempBytes = cipher.doFinal(secretTextDecoded);
            return new String(tempBytes);
        } catch (Exception ex) {
            throw new RuntimeException("【RsaUtils】使用【RSA】解密字符串【" + secretText + "】异常", ex);
        }
    }

    private static byte[] readFile(String fileName) {
        try {
            File file = new File(fileName);
            if (file.exists())
                return Files.readAllBytes(file.toPath());
            else {
                InputStream resourceAsStream = RsaUtils.class.getClassLoader().getResourceAsStream(fileName);
                assert resourceAsStream != null;
                return IoUtil.readBytes(resourceAsStream);
            }
        } catch (IOException ex) {
            throw new RuntimeException("【RsaUtils】读取文件异常", ex);
        }
    }

    private static void writeFile(String destPath, byte[] bytes) {
        try {
            File dest = new File(destPath);
            if (!dest.exists())
                dest.createNewFile();
            Files.write(dest.toPath(), bytes);
        } catch (IOException ex) {
            throw new RuntimeException("【RsaUtils】写入文件异常", ex);
        }
    }

}
